/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { X2DBeta } = require("../../../engine/graphics/X2DInterface");
const { Scr } = require("../../../engine/XDefine");
const { XTools } = require("../../../engine/XTools");
const { RosNode } = require("./classes/RosNode");
const { RosTopic } = require("./classes/RosTopic");
const { RosService } = require("./classes/RosService");
const { XButton } = require("../../../engine/control/XButton");
const { PackageTool } = require("../../ros_msg_utils/PackageTool");
const { WsClient2 } = require("../../../engine/ws/WsClient2");
const { RosProto } = require("../../ros_msg_utils/RosProto");
const { EnvManager } = require("../EnvManager");
const { ProjectConfig } = require("../ProjectConfig");

function generateSmoothCurve(x1, y1, x2, y2, numPoints = 100) {
    numPoints = parseInt(Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) / 30) + 1;
    // 计算控制点
    const delta = Math.abs(x2 - x1) * 0.3; // 控制点偏移量，可以根据需要调整
    const c1 = { x: x1 + delta, y: y1 }; // 控制点1，满足点1出发角度为0度
    const c2 = { x: x2 - delta, y: y2 }; // 控制点2，满足点2出发角度为180度

    // 生成路径点
    const points = [];
    for (let i = 0; i <= numPoints; i++) {
        let t = i / numPoints;
        // for (let t = 0; t <= 1; t += 1 / numPoints) {
        const x = Math.pow(1 - t, 3) * x1 + 3 * Math.pow(1 - t, 2) * t * c1.x + 3 * (1 - t) * Math.pow(t, 2) * c2.x + Math.pow(t, 3) * x2;
        const y = Math.pow(1 - t, 3) * y1 + 3 * Math.pow(1 - t, 2) * t * c1.y + 3 * (1 - t) * Math.pow(t, 2) * c2.y + Math.pow(t, 3) * y2;
        points.push(x);
        points.push(y);
    }

    return points;
}

class RuntimeManager {
    constructor() {
        this.nodes = {};
        this.topics = {};
        this.services = {};
        this.lock = {
            node: null,
            topic: null,
            service: null
        };
        this.drag = {
            is: false,
            x: 0,
            y: 0
        };
        this.hideOn = true;
        this.btn_hide = new XButton({
            onClicked: () => {
                this.hideOn = !this.hideOn;
                XTools.RUNTIME_UPDATE_LINES = true;
            },
            onDraw: () => {
                let x = Scr.logicw - 100;
                let y = 5;
                let w = 95;
                let h = 20;
                this.btn_hide.move(x, y, w, h);
                if (this.hideOn) {
                    X2DBeta.drawRect(x, y, w, h, 0xff00ff00, 2);
                    X2DBeta.Circle(x + h / 2, y + h / 2, h / 3, h / 3, 0, 360, 0xff00ff00);
                    X2DBeta.drawText("隐藏开", 16, x + w - 5, y + h / 2, 1, 1, 0, -3, -2, 0xff00ff00);
                } else {
                    X2DBeta.drawRect(x, y, w, h, 0xffff0000, 2);
                    X2DBeta.Circle(x + w - h / 2, y + h / 2, h / 3, h / 3, 0, 360, 0xffff0000);
                    X2DBeta.drawText("隐藏关", 16, x + 5, y + h / 2, 1, 1, 0, -1, -2, 0xffff0000);
                }
            }
        });
    }
    static getInstance() {
        if (!RuntimeManager.instance) {
            RuntimeManager.instance = new RuntimeManager();
        }
        return RuntimeManager.instance;
    }
    moveTegather(parent, dx, dy, impl) {//按住ctrl时拖动节点，一起移动
        if (parent.type == "node") {
            for (let pub of parent.pubs) {
                let dontMove = false;
                for (let nn in this.nodes) {
                    if (this.nodes[nn].subs.indexOf(pub) != -1) {//被订阅则不移动
                        dontMove = true;
                        break;
                    }
                }
                if (dontMove) continue;
                this.topics[pub].x += dx;
                this.topics[pub].y += dy;
                if (impl) ProjectConfig.getInstance().setRuntimeAttr(this.topics[pub]);
            }
            for (let srv of parent.srvs) {
                this.services[srv].x += dx;
                this.services[srv].y += dy;
                if (impl) ProjectConfig.getInstance().setRuntimeAttr(this.services[srv]);
            }
        } else if (parent.type == "topic") {
            for (let nn in this.nodes) {
                if (this.nodes[nn].subs.indexOf(parent.name) != -1) {
                    this.nodes[nn].x += dx;
                    this.nodes[nn].y += dy;
                    if (impl) ProjectConfig.getInstance().setRuntimeAttr(this.nodes[nn]);
                    this.moveTegather(this.nodes[nn], dx, dy, impl);
                }
            }
        }
    }
    expand(rosState) {
        let changed = false;
        let topics = [];//收集新的名称列表
        let services = [];
        for (let nn in rosState) {
            if (!this.nodes[nn]) {
                this.nodes[nn] = new RosNode(nn, rosState[nn]["pub"], rosState[nn]["sub"], rosState[nn]["srv"]);
                changed = true;
            }
            if (this.nodes[nn].pubs != rosState[nn]["pub"] || this.nodes[nn].subs != rosState[nn]["sub"] || this.nodes[nn].srvs != rosState[nn]["srv"]) {
                this.nodes[nn].pubs = rosState[nn]["pub"];
                this.nodes[nn].subs = rosState[nn]["sub"];
                this.nodes[nn].srvs = rosState[nn]["srv"];
                changed = true;
            }
            for (let pub of rosState[nn]["pub"]) {
                if (!this.topics[pub]) {
                    this.topics[pub] = new RosTopic(pub);
                    changed = true;
                }
                topics.push(pub);
            }
            for (let sub of rosState[nn]["sub"]) {
                if (!this.topics[sub]) {
                    this.topics[sub] = new RosTopic(sub);
                    changed = true;
                }
                topics.push(sub);
            }
            for (let srv of rosState[nn]["srv"]) {
                if (!this.services[srv]) {
                    this.services[srv] = new RosService(srv);
                    changed = true;
                }
                services.push(srv);
            }
        }
        //不在列表中的要清理掉
        for (let nn in this.nodes) {
            if (!rosState[nn]) {
                delete this.nodes[nn];
                changed = true;
            }
        }
        for (let tt in this.topics) {
            if (topics.indexOf(tt) == -1) {
                delete this.topics[tt];
                changed = true;
            }
        }
        for (let ss in this.services) {
            if (services.indexOf(ss) == -1) {
                delete this.services[ss];
                changed = true;
            }
        }
        //初始化节点位置
        let nodex = Scr.logicw / 4;
        let nodey = Scr.logich / 3;
        for (let nn in this.nodes) {
            if (this.nodes[nn].moved) continue;
            while (this.isOverlap(nodex, nodey)) nodey += 50;
            this.nodes[nn].move(nodex, nodey);
            // 由此node发布的topic和service的位置，跟在这个节点后面
            let count = this.nodes[nn].pubs.length + this.nodes[nn].srvs.length;
            let dx = nodex + 250;
            let dy = nodey - count * 50 / 2;
            for (let pub of this.nodes[nn].pubs) {
                if (pub in this.topics && !this.topics[pub].moved) {
                    while (this.isOverlap(dx, dy)) dy += 50;
                    this.topics[pub].move(dx, dy);
                }
                dy += 50;
            }
            for (let srv of this.nodes[nn].srvs) {
                if (srv in this.services && !this.services[srv].moved) {
                    while (this.isOverlap(dx, dy)) dy += 50;
                    this.services[srv].move(dx, dy);
                }
                dy += 50;
            }
            nodey += 50;
        }
        let topicx = Scr.logicw / 2;
        let topicy = Scr.logich / 6;
        for (let tt in this.topics) {
            if (this.topics[tt].moved) continue;
            while (this.isOverlap(topicx, topicy)) topicy += 50;
            this.topics[tt].move(topicx, topicy);
            topicy += 50;
        }
        let servicex = Scr.logicw * 3 / 4;
        let servicey = Scr.logich / 6;
        for (let ss in this.services) {
            if (this.services[ss].moved) continue;
            while (this.isOverlap(servicex, servicey)) servicey += 50;
            this.services[ss].move(servicex, servicey);
            servicey += 50;
        }
        if (changed) {
            XTools.RUNTIME_UPDATE_LINES = true;
        }
    }
    //和其他块重叠
    isOverlap(x, y) {
        for (let nn in this.nodes) {
            if (this.nodes[nn].moved && XTools.InRect(x, y, this.nodes[nn].x - 200, this.nodes[nn].y - 40, 400, 80)) {
                return true;
            }
        }
        for (let tt in this.topics) {
            if (this.topics[tt].moved && XTools.InRect(x, y, this.topics[tt].x - 200, this.topics[tt].y - 40, 400, 80)) {
                return true;
            }
        }
        for (let ss in this.services) {
            if (this.services[ss].moved && XTools.InRect(x, y, this.services[ss].x - 200, this.services[ss].y - 40, 400, 80)) {
                return true;
            }
        }
        return false;
    }
    generateNodeLines() {
        for (let nn in this.nodes) {
            if (this.hideOn && this.nodes[nn].hide) continue;
            this.nodes[nn].pub_lines = {};
            this.nodes[nn].sub_lines = {};
            this.nodes[nn].srv_lines = {};
            for (let topic of this.nodes[nn].pubs) {
                if (topic in this.topics) {
                    if (this.hideOn && this.topics[topic].hide) continue;
                    this.nodes[nn].pub_lines[topic] = generateSmoothCurve(this.nodes[nn].outX2(), this.nodes[nn].outY2(), this.topics[topic].inX(), this.topics[topic].inY());
                }
            }
            for (let topic of this.nodes[nn].subs) {
                if (topic in this.topics) {
                    if (this.hideOn && this.topics[topic].hide) continue;
                    this.nodes[nn].sub_lines[topic] = generateSmoothCurve(this.topics[topic].outX(), this.topics[topic].outY(), this.nodes[nn].inX(), this.nodes[nn].inY());
                }
            }
            for (let service of this.nodes[nn].srvs) {
                if (service in this.services) {
                    if (this.hideOn && this.services[service].hide) continue;
                    this.nodes[nn].srv_lines[service] = generateSmoothCurve(this.nodes[nn].outX(), this.nodes[nn].outY(), this.services[service].inX(), this.services[service].inY());
                }
            }
        }
    }
    drawXLine(ps, c, selected = false) {
        for (let i = 0; i < ps.length - 2; i += 2) {
            // if (selected) {
            //     X2DBeta.drawLine(ps[i] + XTools.RUNTIME_OX, ps[i + 1] + XTools.RUNTIME_OY, ps[i + 2] + XTools.RUNTIME_OX, ps[i + 3] + XTools.RUNTIME_OY, c | 0xf0f0f0, 3);
            // }
            X2DBeta.drawLine(ps[i] + XTools.RUNTIME_OX, ps[i + 1] + XTools.RUNTIME_OY, ps[i + 2] + XTools.RUNTIME_OX, ps[i + 3] + XTools.RUNTIME_OY, c, selected ? 4 : 2);
            // if (parseInt(XTools.TIME_MS / 50) % 10 == i / 2 % 10) {
            //     X2DBeta.drawLine(ps[i] + XTools.RUNTIME_OX, ps[i + 1] + XTools.RUNTIME_OY, ps[i + 2] + XTools.RUNTIME_OX, ps[i + 3] + XTools.RUNTIME_OY, 0xffffffff, 2);
            // }
            // X2DBeta.drawLine(ps[i] + XTools.RUNTIME_OX, ps[i + 1] + XTools.RUNTIME_OY, ps[i + 2] + XTools.RUNTIME_OX, ps[i + 3] + XTools.RUNTIME_OY, selected ? c + 0x00101010 : c, selected ? 2 : 1);
            this.count += 1;
        }
        let dx = ps[ps.length - 2] + XTools.RUNTIME_OX;
        let dy = ps[ps.length - 1] + XTools.RUNTIME_OY;
        X2DBeta.drawLine(dx, dy, dx - 8, dy - 4, c, 1);
        X2DBeta.drawLine(dx, dy, dx - 8, dy + 4, c, 1);
    }
    onDraw() {
        if (XTools.RUNTIME_UPDATE_LINES) {
            this.generateNodeLines();
            XTools.RUNTIME_UPDATE_LINES = false;
        }
        this.count = 0;
        for (let nn in this.nodes) {
            if (this.hideOn && this.nodes[nn].hide) continue;
            for (let ps in this.nodes[nn].pub_lines) {
                this.drawXLine(this.nodes[nn].pub_lines[ps], 0xff7bed9f, this.lock.node == nn || this.lock.topic == ps);
            }
            for (let ps in this.nodes[nn].sub_lines) {
                this.drawXLine(this.nodes[nn].sub_lines[ps], 0xffeccc68, this.lock.node == nn || this.lock.topic == ps);
            }
            for (let ps in this.nodes[nn].srv_lines) {
                this.drawXLine(this.nodes[nn].srv_lines[ps], 0xff70a1ef, this.lock.node == nn || this.lock.service == ps);
            }
        }

        for (let nn in this.nodes) {
            if (this.lock.node == nn) continue;
            if (this.hideOn && this.nodes[nn].hide) continue;
            this.nodes[nn].onDraw(XTools.RUNTIME_OX, XTools.RUNTIME_OY);
        }
        for (let tt in this.topics) {
            if (this.lock.topic == tt) continue;
            if (this.hideOn && this.topics[tt].hide) continue;
            this.topics[tt].onDraw(XTools.RUNTIME_OX, XTools.RUNTIME_OY);
        }
        for (let ss in this.services) {
            if (this.lock.service == ss) continue;
            if (this.hideOn && this.services[ss].hide) continue;
            this.services[ss].onDraw(XTools.RUNTIME_OX, XTools.RUNTIME_OY);
        }
        {//画外边框和操作按钮
            if (this.lock.node) {
                if (this.lock.node in this.nodes && !(this.hideOn && this.nodes[this.lock.node].hide)) {
                    let n = this.nodes[this.lock.node];
                    n.onDraw(XTools.RUNTIME_OX, XTools.RUNTIME_OY, true);
                    X2DBeta.drawRect(n.x + XTools.RUNTIME_OX, n.y + XTools.RUNTIME_OY, n.w, n.h, 0xff0000ff, 2);
                }
                else this.lock.node = null;
            }
            if (this.lock.topic) {
                if (this.lock.topic in this.topics && !(this.hideOn && this.topics[this.lock.topic].hide)) {
                    let t = this.topics[this.lock.topic];
                    t.onDraw(XTools.RUNTIME_OX, XTools.RUNTIME_OY, true);
                    X2DBeta.drawRect(t.x + XTools.RUNTIME_OX, t.y + XTools.RUNTIME_OY, t.w, t.h, 0xff00ff00, 2);
                    if (!t.msgType) {
                        RosProto.queryTopicMsgType(this.lock.topic);
                        t.msgType = "querying..."
                    }
                }
                else this.lock.topic = null;
            }
            if (this.lock.service) {
                if (this.lock.service in this.services && !(this.hideOn && this.services[this.lock.service].hide)) {
                    let s = this.services[this.lock.service];
                    s.onDraw(XTools.RUNTIME_OX, XTools.RUNTIME_OY, true);
                    X2DBeta.drawRect(s.x + XTools.RUNTIME_OX, s.y + XTools.RUNTIME_OY, s.w, s.h, 0xffff0000, 2);
                }
                else this.lock.service = null;
            }
        }
        this.btn_hide.onDraw();
    }
    onTouch(msg, x, y) {
        if (this.btn_hide.onTouch(msg, x, y)) return true;
        if (this.drag.is) {
            if (msg == 2) {
                XTools.RUNTIME_OX -= this.drag.x - x;
                XTools.RUNTIME_OY -= this.drag.y - y;
                this.drag.x = x;
                this.drag.y = y;
                return true;
            }
            this.drag.is = false;
            return false;
        }
        //拖动窗口后，需要更新连线
        if (this.lock.node) {
            if (this.nodes[this.lock.node].onTouch(msg, x - XTools.RUNTIME_OX, y - XTools.RUNTIME_OY, true, this.hideOn)) {
                XTools.RUNTIME_UPDATE_LINES = true;
                return true;
            }
        }
        if (this.lock.topic) {
            if (this.topics[this.lock.topic].onTouch(msg, x - XTools.RUNTIME_OX, y - XTools.RUNTIME_OY, true, this.hideOn)) {
                XTools.RUNTIME_UPDATE_LINES = true;
                return true;
            }
        }
        if (this.lock.service) {
            if (this.services[this.lock.service].onTouch(msg, x - XTools.RUNTIME_OX, y - XTools.RUNTIME_OY, true, this.hideOn)) {
                XTools.RUNTIME_UPDATE_LINES = true;
                return true;
            }
        }
        //选中节点
        // for (let service in this.services) {
        for (let service of Object.keys(this.services).reverse()) {
            if (this.services[service].onTouch(msg, x - XTools.RUNTIME_OX, y - XTools.RUNTIME_OY, false, this.hideOn)) {
                this.lock = {
                    node: null,
                    topic: null,
                    service: service
                };
                return true;
            }
        }
        for (let topic in this.topics) {
            if (this.topics[topic].onTouch(msg, x - XTools.RUNTIME_OX, y - XTools.RUNTIME_OY, false, this.hideOn)) {
                this.lock = {
                    node: null,
                    topic: topic,
                    service: null
                };
                return true;
            }
        }
        for (let nn in this.nodes) {
            if (this.nodes[nn].onTouch(msg, x - XTools.RUNTIME_OX, y - XTools.RUNTIME_OY, false, this.hideOn)) {
                this.lock = {
                    node: nn,
                    topic: null,
                    service: null
                };
                return true;
            }
        }
        if (msg != 2) {//取消选中节点
            this.lock = {
                node: null,
                topic: null,
                service: null
            }
        }
        if (msg == 1) {//开始全局拖动
            this.drag = {
                is: true,
                x: x,
                y: y
            }
            return true;
        }
        return false;
    }
    onKey(type, k) {
        if ("/cmd_vel" in this.topics) {
            if (!this.msg) {
                this.msg = {
                    linear: {
                        x: 0, // float64
                        y: 0, // float64
                        z: 0, // float64
                    }, // geometry_msgs/Vector3
                    angular: {
                        x: 0, // float64
                        y: 0, // float64
                        z: 0, // float64
                    }, // geometry_msgs/Vector3
                };
            }
            if (k == "ArrowUp") {
                if (type == 1 && this.msg.linear.x != 1) {//按下向前
                    this.msg.linear.x = 1;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.linear.x = 0;
                } else if (type == 0 && this.msg.linear.x == 1) {//松开向前
                    this.msg.linear.x = 0;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.linear.x = 1;
                }
            }
            else if (k == "ArrowDown") {
                if (type == 1 && this.msg.linear.x != -1) {//按下向后
                    this.msg.linear.x = -1;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.linear.x = 0;
                } else if (type == 0 && this.msg.linear.x == -1) {//松开向后
                    this.msg.linear.x = 0;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.linear.x = -1;
                }
            }
            else if (k == "ArrowLeft") {
                if (type == 1 && this.msg.angular.z != 3) {//按下向左
                    this.msg.angular.z = 3;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.angular.z = 0;
                } else if (type == 0 && this.msg.angular.z != 0) {//松开向左
                    this.msg.angular.z = 0;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.angular.z = 3;
                }
            }
            else if (k == "ArrowRight") {
                if (type == 1 && this.msg.angular.z != -3) {//按下向右
                    this.msg.angular.z = -3;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.angular.z = 0;
                } else if (type == 0 && this.msg.angular.z != 0) {//松开向右
                    this.msg.angular.z = 0;
                    if (!topic.publish("/cmd_vel", this.msg, 0)) this.msg.angular.z = -3;
                }
            }
        }
        if (type == 0) return false;
        if (this.lock.node) {
            if (this.nodes[this.lock.node].onKey(type, k)) {
                XTools.RUNTIME_UPDATE_LINES = true;
                return true;
            }
        }
        if (this.lock.topic) {
            if (this.topics[this.lock.topic].onKey(type, k)) {
                XTools.RUNTIME_UPDATE_LINES = true;
                return true;
            }
        }
        if (this.lock.service) {
            if (this.services[this.lock.service].onKey(type, k)) {
                XTools.RUNTIME_UPDATE_LINES = true;
                return true;
            }
        }
        return false;
    }
}

module.exports = {
    RuntimeManager
}